// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: declaration of CXFormsElement.
//   $Id$


// XFormsElement.h: interface for the CXFormsElement class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_XFORMSELEMENT_H__68F1F737_007F_4ED2_9D8F_F12FC8A60201__INCLUDED_)
#define AFX_XFORMSELEMENT_H__68F1F737_007F_4ED2_9D8F_F12FC8A60201__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000


#include "BinaryBehavior.h"  
#include "Parse.h"
#include "EventSink.h"

#include "XForms.h"
#include "DOM3Core.h"
#include "UIInterfaces.h"

#include "IUIEvents.h"
#include "CSSConst.h"
#include "resource.h" 

#include "CComBSTRPtrList.h"

class CImpl;
class CFormControl;
class CXFormsParse;
struct IEventSink;
class CXFormsXFormsElement;
#include <DOM2Core.h>
#include "DeletionListener.h"

#define CXFORMSELEMENT_EVENTS_LENGTH 19

//#define COUNT_CXFORMSELEMENT 1

const BSTR 	g_bsBlankURL = ::SysAllocString(L"about:blank");

//TODO:
//This class needs restructuring.  Some of the action starts too 
//	high in the inheritance tree, and assumes that all derived
//	classes are form controls.
//
//	Repeat and model are not form controls.
class ATL_NO_VTABLE CXFormsElement :
	public CBinaryBehavior
	,public IObjectWithForm
//	,public IUIEvents
	,public ICurrentValue
	,public IDeletionListener
{

public:
#ifdef COUNT_CXFORMSELEMENT
	static unsigned long sm_Count;
#endif
	DECLARE_GET_CONTROLLING_UNKNOWN();
	BEGIN_COM_MAP(CXFormsElement)
		COM_INTERFACE_ENTRY(ICurrentValue)
		COM_INTERFACE_ENTRY(IObjectWithForm)
		COM_INTERFACE_ENTRY2(IUnknown,IElementBehavior)
		COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_INode,m_pDOM2NodeImpl.p,CLSID_Node)
		COM_INTERFACE_ENTRY_CHAIN(CBinaryBehavior)
	END_COM_MAP()
#ifdef _DEBUG
	HRESULT InternalQueryInterface(void * pThis,const _ATL_INTMAP_ENTRY *pEntries,REFIID iid, void**ppvObject)
	{
		HRESULT hr = __super::InternalQueryInterface(pThis,pEntries,iid,ppvObject);
		return hr;
	}
#endif
	HRESULT FinalConstruct();
	void FinalRelease();
	//Because this class stores an extra pointer to 
	//	an aggregated object, finalRelease should
	//	occur at refcount==1
	ULONG InternalRelease(void);
	STDMETHOD(addEventListener)(BSTR type, IEventListener * listener, BOOL useCapture);
	STDMETHOD(removeEventListener)(BSTR type, IEventListener * listener, BOOL useCapture);
	STDMETHOD(dispatchEvent)(IDOMEvent * evt,BOOL * bCancelled);
	//Interface IUIEvents.

	STDMETHOD(get_CurrentValue)(BSTR *psCurrentValue);

	virtual HRESULT InitBehaviour();

	inline void formControlUnInitialize();
	virtual HRESULT required(BOOL bRequired);
	virtual HRESULT focus();
	HRESULT showFocus(bool bFocus);
	bool m_bFocus;
	virtual HRESULT highlight(bool bApply,bool bFocus);
	CXFormsElement();
	virtual ~CXFormsElement();
	bool m_bElementRendered;
	//HRESULT _ElementReady();
	STDMETHOD(Detach)();
	STDMETHOD(GetProperty)(BSTR sProperty, BSTR * sValue);
	STDMETHOD(SetProperty)(BSTR sProperty, BSTR sValue);

	virtual HRESULT ElementReady();
	virtual HRESULT DocumentReady();
	virtual HRESULT enable(BOOL bShow);
	// <summary>applies readonly/readwrite style</summary>
	virtual HRESULT readonly(BOOL bReadOnly);
	// Bug 1021 - Need to overload this for repeats
	virtual HRESULT valid(BOOL bValid);
	virtual HRESULT valueChangedEffect();
	virtual HRESULT OutOfRangeEffect();
	virtual HRESULT InRangeEffect();
	virtual HRESULT SelectedEffect();
	virtual HRESULT DeselectedEffect();

	virtual CImpl* CreateImpl();
	virtual CXFormsParse* CreateParse(CImpl* pImpl);
	//	TODO:
	//Should these be pure virtual?
	virtual HRESULT get_valuechanging(BSTR* psValue);
	virtual HRESULT get_value(BSTR* psValue);// = 0;
	virtual HRESULT put_value(BSTR sValue);// = 0;
	virtual HRESULT attachEvents(IHTMLDocument3 * pDoc3 = 0);// = 0;
	virtual HRESULT attachEvents(IHTMLElement2 * pEl2);

	virtual HRESULT handleEvent(DISPID eventID,VARIANT* pvarResult,DISPPARAMS* pdispparams);
	virtual HRESULT formControlInitialize();
	virtual HRESULT resetScriptDispID();

//was protected, then needed in XFormsXFormsElement, potentially dangerous, 
//	maybe XFXF should be friend?
	//pointer to the UI Representation of the actual control.
	CComPtr<IHTMLElement> m_pControl;
//	CComObject<CXFormsXFormsElement>* m_pXForms;
	CXFormsXFormsElement * m_pXForms;
	long m_lGUIStringID;
		// to be called at the end of the refresh phase, to allow any display updates that have not occured to occur.
	inline HRESULT updateAppearance(void);
	// Insert navIndex & accesskey into  UI Object.
	HRESULT put_navindex(BSTR bsValue);
	HRESULT put_accesskey(BSTR bsValue);
	virtual HRESULT put_XPathExpression(const BSTR bsXPath);

	// <summary>resolves m_pParentImpl for the bound CImpl... object</summary>
	HRESULT ResolveEnclosingBinding(IHTMLElement * pMasterElement,CImpl ** ppImpl);
	HRESULT ResolveEnclosingBinding(CImpl ** ppImpl);
	HRESULT ResolveEnclosingBinding();


	STDMETHOD(get_formKey)(LONG* pVal);
	STDMETHOD(put_formKey)(LONG newVal);
	long getParentOrdinal();
	HRESULT get_ModelId(BSTR * psModel);
	//The MIPProxty element is one that will receive MIP classes alongside the proper element
	//	It is used to allow messages to behave differently when within (For Example)  an invalid control
	//	vs. a valid one
	HRESULT put_MIPProxyElement(IHTMLElement * pEl);
	HRESULT get_Impl(CImpl**pImpl)
	{
		*pImpl = m_pImpl;
		return S_OK;
	}
	virtual void OnDeleteOfSubject();
	/// <summary>Set the inital state of the css classes for this element. This is called from within 
	/// CBoundNode::AddTarget() through the IBoundTarget::setInitialState().</summary>
	virtual HRESULT SetClassProperty(bool bEnabled, bool bReadOnly, bool bValid, bool bRequired);
	void setBindingToTarget(bool bBinding);
	virtual HRESULT changeUI();
protected:
	HRESULT ConcealCaretIfWithinSelf();
	virtual void RegisterFindBehaviourAcceptance();
	void StyleUnHoverishly();
	void StyleHoverishly();
	bool m_bDocumentReady;
	virtual HRESULT detachEvents();
	HRESULT RegisterCustomEvents(const CComBSTR * const aEventNames[],long lCount, long aCookies[]);
	virtual HRESULT handleEvent(CComBSTR& sEventName, IHTMLEventObj2 * pEventObject,VARIANT * pvarResult);
	HRESULT put_style(BSTR sWhere, BSTR sStyle);
	char * m_sControlName;
	//void createPath();
	CComVariant m_vtControlFile;
	bool m_bNewMethod;
	bool m_bUpdatePending;
	CComPtr<IHTMLElement> m_pControlElement;
	CComPtr<IHTMLWindow2> m_pControlWin;
	//pointer to the UI Representation of the span containing the control.
	CComPtr<IHTMLElement> m_pMain;


	virtual HRESULT getUI(CComBSTR &sHtml);
	virtual HRESULT getSignificantDescendentNodes(IHTMLDocument3 * pDoc3);
	virtual HRESULT copyStyle();
	DWORD m_dwCookie;
	CImpl * m_pImpl;
	CFormControl * m_pFormControl;
	//This has moved from a local variable in render. 
	// This makes style copying easier, as we can call
	//	getElementsByID on this object to get misc children.
	//local again, can just get it from m_pDefaults
	//	IHTMLDocument * m_pViewLinkDoc;
	
	GUID m_guidEvents;
	
	CComQIPtr<IEventSink> m_pEventSink;
	virtual HRESULT render();
	// Escapes a string argument for use as a JScript parameter, removing newlines and escaping single quotes
	void escapeJScriptArgument(CStringW & rsArg) const;
	// executes a script on the UI.
	HRESULT UIExecScript(BSTR bsScript);
	//calls a function in the JScript
	//<param name="pParams">Don't forget that DISPPARAMS is reversed.</param>
	//<param name="dispid">
	//	in/out parameter to allow you to cache the result
	//	of getIdsOfNames.
	//</param>
	HRESULT UIExecFunction(BSTR bsFunctionName,DISPPARAMS *pParams,VARIANT *  pvtResult,DISPID & dispid);
	HRESULT UIExecFunction(IDispatch * pDisp, BSTR bsFunctionName,DISPPARAMS * pParams,VARIANT * pvtResult,DISPID & dispid);
	//Used in attach- and detachEvents to attach to the 
	//	appropriate events in the HTML UI.
	CComBSTRPtrList m_lstEventNames;
	//Called to fill the HTML event handler list of names
	virtual void populateEvents(void);
	BOOL m_bEnabled;
	BOOL m_bValid;
	BOOL m_bRequired;
	BOOL m_bVisited;
	// Fetches and writes the UI for this control into the given document
	virtual HRESULT WriteUI(IHTMLDocument2 * pDoc2);
	CStringW m_sDebugInfo;

	// Controls that don't want branding behaviour should implement this to do nothing
	virtual HRESULT InsertBranding(void);
	HRESULT InvokeDefaultUIBehaviour(IDOMEvent * pEvent);
	HRESULT HTMLContains(IHTMLElement * pEl,bool * pbResult) const;

	LONG m_lFormKey;

	// Bug 503: Used to prevent processing keyup events on the <enter> key for buttons,
	//          because pressing <enter> on a button fires the "click" event as well.
	bool m_bIsButton;

	// Bug 962 - Used by derived classes.
	bool m_bPuttingValue;
	DISPID m_didPutValue;
	HRESULT GetElementForBranding(CComPtr<IHTMLElement> &pEl, bool &bIsNonViewlinkWithNoLabel);

	// <summary>replaces an occurrence of sReplacing with sSetting in the className property.  If no occurrence of sReplacing is found, concatenates sSetting to the className</summary>
	HRESULT SetClassProperty(IHTMLElement * pEl,BSTR sSetting, BSTR  sReplacing);

	CComQIPtr<IHTMLElement3> m_pDisablee;

	/// <summary>Keep a copy of the current value of the xforms element. We can use this to determine 
	/// whether we actually need to pass the value through to the uiscript using put_value()</summary>
	CComBSTR m_sCurrentValue;

	bool m_bElementProcessed;
	
private:
	CComPtr<INode> m_pDOM2NodeImpl;
	BSTR wrapUI(CComBSTR &sUI);
	HRESULT processNavIndex(IHTMLElement * pMaster);
	HRESULT processAccessKey(IHTMLElement * pMaster);

	virtual HRESULT putDisabledValue();
	HRESULT SetClassPropertyOnViewlinkBody(BSTR sSetting, BSTR  sReplacing);

	// <value>set to true whilst values are being written into the UI, to prevent writeback from occuring.</value>
	BOOL m_bReadonly;

	HRESULT put_XPathExpression();
	HRESULT SetBrandingMouseoverAttribute(const wchar_t * const lpwsz);
	HRESULT GetFirstNamedElementFromDocument(IHTMLDocument *pDoc, const wchar_t * const lpwszElementName, CComPtr<IHTMLElement> &pEl);
	HRESULT onBeforeEventExecCommand(BSTR sCmdID);

	//array storing event cookies.
	//	these are the ids given to us by 
	//	RegisterEvent.
	long m_alEventCookies[CXFORMSELEMENT_EVENTS_LENGTH];
	//The event cookies are assigned on an
	//	object-by-object basis, however, 
	//	the names can be static.
	static const CComBSTR * const sm_asEventNames[CXFORMSELEMENT_EVENTS_LENGTH];

	DISPID m_didPutFocus;
	DISPID m_didPutStyle;
	DISPID m_didExecCommand;
	DISPID m_dispidActivate;

	CComPtr<IEventTarget> m_pEventTargetImpl;
	CComPtr<IUnknown>  m_pUnkInnerEventTarget;
	
	//prevents ResolveEnclosingBinding being called
	//	multiple times on elements with no enclosing binding
	bool m_bEnclosingBindingResolved;
	HRESULT getEventTargetImpl(IEventTarget ** ppTargetImpl);
	HRESULT addXblParentNode(IHTMLDocument * pDoc, IHTMLElement * pMaster);
	HRESULT addHostDocumentProperty(IHTMLDocument * pDoc, IDispatch * pHostDocument);
	CComPtr<IClassFactory> m_pEventEventFactory;
	HRESULT CreateEventEvent(IEventEvent **ppEventEvent);
	//See the put_MIPProxyElement method for more information.
	CComPtr<IHTMLElement> m_pMIPProxy;
	/// <summary>Indicates whether the element is being bound to a node. This is set to true at the start of
	/// IBoundTarget::addTarget and false at the end</summary>
	bool m_bBindingToTarget;
	CComBSTR m_sFormerDisplayProperty;
	HRESULT StopAddingControlsAndInitialize();
	bool m_bXFELEventsAttached;


};

inline void CXFormsElement::setBindingToTarget(bool bBinding) {
	m_bBindingToTarget = bBinding;

}

inline HRESULT CXFormsElement::CreateEventEvent(IEventEvent **ppEventEvent) {
	HRESULT hr = S_FALSE;
	
	if(!m_pEventEventFactory) 
		hr = ::CoGetClassObject(CLSID_EventEvent, CLSCTX_ALL, 0, IID_IClassFactory, reinterpret_cast<void **>(&m_pEventEventFactory));

	if(m_pEventEventFactory)
		hr = m_pEventEventFactory->CreateInstance(0, IID_IEventEvent, reinterpret_cast<void **>(ppEventEvent));
	
	return hr;
}

inline void CXFormsElement::formControlUnInitialize()
{
	m_pImpl = 0;
	m_pFormControl = 0;
	if(m_pEventSink)
	{
		detachEvents();
		m_pEventSink.Release();
	}
}

// to be called at the end of the refresh phase, to allow any display updates that have not occured to occur.
inline HRESULT CXFormsElement::updateAppearance(void)
{
	HRESULT hr = S_FALSE;

	// copyStyle must be called once the DocumentReady has been recieved by 
	// the behaviour. This is due to repeat items being added dynamically and in
	// some cases crashes on the get_XXX function call on the currentStyle object
	// Fixes Bug 271
	if(m_bUpdatePending && (m_pControl || m_pControlWin) && m_bDocumentReady) {
		m_bUpdatePending = false;
		hr = copyStyle();
	}
	return hr;
}

HRESULT SetClassPropertyOnElement(IHTMLElement * pEl, bool bEnabled, bool bReadOnly, bool bValid, bool bRequired,bool * pbModified);
#endif // !defined(AFX_XFORMSELEMENT_H__68F1F737_007F_4ED2_9D8F_F12FC8A60201__INCLUDED_)

